﻿using System;
using System.Net;
using System.Web;
using System.Globalization;
using System.ServiceModel.Web;
using Stimulus360.Models.Data;
using Microsoft.ServiceModel.Web;
using System.Collections.Generic;
using System.ServiceModel.Activation;
using System.ServiceModel.Syndication;
using Stimulus360.Services.Interfaces;

namespace Stimulus360.Services
{
    /// <summary>
    ///      This class is used to provide RSS service for Projects and Ideas.
    /// </summary>
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class FeedsService : IFeedsService
    {
        /// <summary>
        ///     Instance of IdeaService to be used for Idea related feeds
        /// </summary>
        private readonly IIdeaService _ideaService;

        /// <summary>
        ///     Instance of Project Service to be used for Project related feeds
        /// </summary>
        private readonly IProjectService _projectService;

        /// <summary>
        ///     Returns the host name from request Url.
        /// </summary>
        private static string Host
        {
            get
            {
                string host = string.Empty;
                if (HttpContext.Current != null)
                    host = HttpContext.Current.Request.Url.Scheme + "://" + HttpContext.Current.Request.Headers["Host"];
                else if (WebOperationContext.Current != null)
                    host = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.BaseUri.Scheme + "://" + WebOperationContext.Current.IncomingRequest.Headers["Host"];
                return host;
            }
        }

        /// <summary>
        ///     Defautl Constructor.Instantiates the services
        /// </summary>
        public FeedsService()
        {
            try
            {
                _ideaService = new IdeaService();
                _projectService = new ProjectService();
            }
            catch (NullReferenceException exception)
            {
                // Exception conditions detected when invoking this service as a RESTFul service need to report an HTTP status code in the 
                //  400-500   range in addition to a message that details the exception
                throw new WebProtocolException(HttpStatusCode.BadGateway, "Could not connect to the database.Please check the connection and try again", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to provide feed of new ideas
        /// </summary>
        /// <param name="format">
        ///     Feed format
        /// </param>
        /// <returns>
        ///     Feed of new ideas
        /// </returns>
        public SyndicationFeedFormatter NewIdeas(string format)
        {
            try
            {
                SyndicationFeed feed = new SyndicationFeed("New ideas feed", "New Ideas Feed", new Uri(Host));
                feed.Generator = Host;

                feed.Categories.Add(new SyndicationCategory("Ideas"));
                feed.Description = new TextSyndicationContent("New Ideas");

                IEnumerable<Idea> ideaList = _ideaService.GetNew();

                feed.Items = GetSyndicationItems(ideaList);

                if (format == "atom")
                {
                    return new Atom10FeedFormatter(feed);
                }
                return new Rss20FeedFormatter(feed);
            }
            catch (ArgumentException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The \"format\" provided is invalid, expected values atom or rss", exception );
            }
            catch (UriFormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The Host URL is not valid", exception);
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError , "One or more objects was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to provide feed of most popular ideas
        /// </summary>
        /// <param name="format">
        ///     Feed format
        /// </param>
        /// <returns>
        ///     Feed of most popular ideas
        /// </returns>
        public SyndicationFeedFormatter MostPopularIdeas(string format)
        {
            try
            {
                SyndicationFeed feed = new SyndicationFeed("Most popular ideas feed", "Most popular ideas feed", new Uri(Host));
                feed.Generator = Host;

                feed.Categories.Add(new SyndicationCategory("Most Popular Ideas"));
                feed.Description = new TextSyndicationContent("Most Popular Ideas");

                IEnumerable<Idea> ideaList = _ideaService.GetTopN("20");

                feed.Items = GetSyndicationItems(ideaList);

                if (format == "atom")
                {
                    return new Atom10FeedFormatter(feed);
                }
                return new Rss20FeedFormatter(feed);
            }
            catch (ArgumentException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The \"format\" provided is invalid, expected values atom or rss", exception);
            }
            catch (UriFormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The Host URL is not valid", exception);
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError , "One or more objects was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to provide feed of new projects
        /// </summary>
        /// <param name="format">
        ///     Feed format
        /// </param>
        /// <returns>
        ///     Feed of new projects
        /// </returns>
        public SyndicationFeedFormatter NewProjects(string format)
        {
            try
            {
                SyndicationFeed feed = new SyndicationFeed("New Projects feed", "New Projects Feed", new Uri(Host));
                feed.Generator = Host;

                feed.Categories.Add(new SyndicationCategory("Projects"));
                feed.Description = new TextSyndicationContent("New Projects");

                IEnumerable<Project> projList = _projectService.GetNew();

                feed.Items = GetSyndicationItems(projList);

                if (format == "atom")
                {
                    return new Atom10FeedFormatter(feed);
                }
                return new Rss20FeedFormatter(feed);
            }
            catch (ArgumentException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The \"format\" provided is invalid, expected values atom or rss", exception);
            }
            catch (UriFormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The Host URL is not valid", exception);
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "One or more objects was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to provide feed of most popular projects
        /// </summary>
        /// <param name="format">
        ///     Feed format
        /// </param>
        /// <returns>
        ///     Feed of most popular Projects
        /// </returns>
        public SyndicationFeedFormatter MostPopularProjects(string format)
        {
            try
            {
                SyndicationFeed feed = new SyndicationFeed("Most popular projects feed", "Most popular projects feed", new Uri(Host));
                feed.Generator = Host;

                feed.Categories.Add(new SyndicationCategory("Most Popular Projects"));
                feed.Description = new TextSyndicationContent("Most Popular Projects");

                IEnumerable<Project> projList = _projectService.GetTopN("20");

                feed.Items = GetSyndicationItems(projList);

                if (format == "atom")
                {
                    return new Atom10FeedFormatter(feed);
                }
                return new Rss20FeedFormatter(feed);
            }
            catch (ArgumentException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The \"format\" provided is invalid, expected values atom or rss", exception);
            }
            catch (UriFormatException exception)
            {
                throw new WebProtocolException(HttpStatusCode.BadRequest , "The Host URL is not valid", exception);
            }
            catch (NullReferenceException exception)
            {
                throw new WebProtocolException(HttpStatusCode.InternalServerError , "One or more objects was null", exception);
            }
            catch (Exception exception)
            {
                throw new WebProtocolException(ExceptionHelper.GetStatusCode(exception), exception.Message, exception);
            }
        }

        /// <summary>
        ///     Method to convert the proj list into syndicated items
        /// </summary>
        /// <param name="projList">
        ///     List of projects
        /// </param>
        /// <returns>
        ///     Corresponding list of syndication items
        /// </returns>
        private static List<SyndicationItem> GetSyndicationItems(IEnumerable<Project> projList)
        {
            List<SyndicationItem> syndicationItems = new List<SyndicationItem>();
            foreach (Project proj in projList)
            {
                SyndicationItem syndicationItem = new SyndicationItem(proj.Title,
                                                                proj.Description,
                                                                new Uri(string.Format(CultureInfo.CurrentCulture ,"{0}/Projects/{1}", Host, proj.ProjectId))
                                                                );
                syndicationItems.Add(syndicationItem);
            }
            return syndicationItems;
        }

        /// <summary>
        ///     Method to convert the idea list into syndicated items
        /// </summary>
        /// <param name="ideaList">
        ///     List of ideas
        /// </param>
        /// <returns>
        ///     Corresponding list of syndication items
        /// </returns>
        private static List<SyndicationItem> GetSyndicationItems(IEnumerable<Idea> ideaList)
        {
            List<SyndicationItem> syndicationItems = new List<SyndicationItem>();
            foreach (Idea idea in ideaList)
            {
                SyndicationItem syndicationItem = new SyndicationItem(idea.Title,
                                                                idea.Description,
                                                                new Uri(string.Format(CultureInfo.CurrentCulture ,"{0}/Ideas/{1}", Host, idea.IdeaId))
                                                                );
                syndicationItem.Authors.Add(new SyndicationPerson(idea.UserProfile.Email, idea.SubmittedBy, "#"));/* fill in with authors's info*/
                syndicationItems.Add(syndicationItem);
            }
            return syndicationItems;
        }
    }
}
